# -*- coding: utf-8 -*-
# Copyright (C) 2007 Boris Dušek
# See the file LICENCE in the top-level directory of this distribution
# for licensing information.
# vim:ts=4:sw=4:et

"""Module for manipulating data as an array of bits."""

bit_masks = tuple([2**i for i in range(64)])

class Endianness:
    """Specifies mapping between sequence of byte chunks and its value."""

    def order(self,chunk1,chunk2,chunk_exp):
        """Returns chunks in order from least significant to most significant.

        This e.g. means that if chunks are bit sequences and these are
        concatenated in the returned order, then the correct bit representation
        (from least significant to most significant bit) is returned of the
        number that is represented in memory by chunk1, chunk2 in this order.

        chunk1 - chunk that is first in memory
        chunk2 - chunk that is right after chunk1 in memory
        chunk_exp - specifies size of chunks; both chunks have size
                    2**chunk_exp

        """
        raise NotImplementedError

    def byte_order(self,bytes,exp):
        """Returns bytes sorted increasingly according to the byte
        significance.

        bytes - string of bytes in the order they appear in memory
        exp - len(bytes) == 2**exp
        """
        assert(len(bytes) == 2**exp)
        if exp < 1:
            raise Error, "exp = %d (<1)" % exp
        else:
            mid = 2 ** (exp-1)
            lo,hi = self.order(bytes[:mid],bytes[mid:],exp-1)
            if exp == 1:
                return lo + hi
            else:
                return self.byte_order(lo,exp-1) + self.byte_order(hi,exp-1)

    def value(self,bytes,exp):
        """Returns integer value of number represented by bytes.

        bytes - sequence of bytes in the same order as in memory
        exp - len(bytes) == 2**exp

        Return value: value of the number represented by bytes.

        """
        sum = 0
        for e,byte in self.byte_order(bytes,exp):
            sum += int(byte) * bit_masks[8*e]
        return sum

    def bits(self,bytes,exp):
        """Returns one array of bits just like in endian.bits, just large
        enough to accomodate all bits. Bits are returned, as in endian.bits,
        in the order from least to most significant.

        bytes - array of bytes in memory in the same order as in memory;
                it's a list of 2**exp bytes from memory
        exp - see bytes

        Return value: array of integers representing bits:
                      1 represents bit set, 0 bit unset
                      bits are ordered from least to most significant
        """
        ret = []
        for byte in self.byte_order(bytes,exp):
            ret += bits(byte)
        return ret

    def _bytes(self,value,exp):
        """Return byte array of value ordered from least significant byte.

        value - value to represent as byte array
        exp - consider only first 2**(8*exp) bytes of value

        Return value: array of bytes such that
                      value = sum(byte*(2**(8*e)) for e,byte in enumerate(ret))

        """
        factor = 2**8
        mask = factor - 1
        ret = []
        for i in range(2**exp):
            ret.append(value & (mask))
            value /= factor
        return ret

    def bytes(self,value,exp):
        """Return memory representation of value.

        value - integer value to represent
        exp - consider value as exp-byte number

        Return value: array of bytes representing the number in memory
                      according to this endianness

        """
        # byte_order ** 2 = Identity
        return self.byte_order(self._bytes(value,exp),exp)

class BigEndianness(Endianness):
    """Mapping where the first chunk is always the most significant."""

    def order(self,chunk1,chunk2,chunk_exp):
        yield chunk2
        yield chunk1

class LittleEndianness(Endianness):
    """Mapping where the first chunk is always the least significant."""

    def order(self,chunk1,chunk2,chunk_exp):
        yield chunk1
        yield chunk2


def bits(byte):
    """Returns a bit array equivalent to the supplied byte.

    byte - byte to represent as bits
    exp - how many bytes does bytes represent; see bytes

    Return value: array of integers. 1 represents bit set, 0 bit unset

    """
    if isinstance(byte,basestring):
        bit_count = 8
    else # int
        bit_count = 32
    return [int((byte & mask) > 0) for mask in bit_masks[:bit_count]]

def value(bits):
    _sum = 0
    for pos,bit in enumerate(bits):
        _sum += bit_masks[pos] * bit
    return _sum

def rotate(bits,amount):
    n = len(bits)
    amount = amount % n
    ret bits[n:] + bits[:n]

def bit(value):
    return int((value & bit_masks[pos]) <> 0)

# Following code is (c) 2002 by Sebastian Keim
# taken from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/113799
# according to the site, everything is licensed under Python License
# unless stated otherwise (and it is not stated otherwise in this case)
class BitField(object):
    def __init__(self,value=0):
        self._d = value

    def __getitem__(self, index):
        return (self._d >> index) & 1 

    def __setitem__(self,index,value):
        value    = (value&1L)<<index
        mask     = (1L)<<index
        self._d  = (self._d & ~mask) | value

    def __getslice__(self, start, end):
        mask = 2L**(end - start) -1
        return (self._d >> start) & mask

    def __setslice__(self, start, end, value):
        mask = 2L**(end - start) -1
        value = (value & mask) << start
        mask = mask << start
        self._d = (self._d & ~mask) | value
        return (self._d >> start) & mask

    def __int__(self):
        return self._d

